package mapEditor.utils;

//import debug.*;

import mapEditor.EditorProperties;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.GeneralPath;

import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.w3c.dom.Element;
import org.w3c.dom.svg.SVGElement;

// DOC: http://www.w3.org/TR/SVG11/painting.html#StrokeProperties
public class SvgStroke extends ValueType
{
  public static final Color   DEFAULT_COLOR=null;
  public static final float[] DEFAULT_DASH_ARRAY=null;
  public static final float   DEFAULT_DASH_OFFSET=0.0f;
  public static final int     DEFAULT_LINE_CAP=BasicStroke.CAP_BUTT; //"butt";
  public static final int     DEFAULT_LINE_JOIN=BasicStroke.JOIN_MITER; //"miter";
  public static final float   DEFAULT_MITER_LIMIT=4.0f;
  public static final float   DEFAULT_OPACITY=1.0f;
  public static final float   DEFAULT_WIDTH=1.0f;
  
  private boolean ENABLED=true;
  private Color COLOR=DEFAULT_COLOR;
  private float[] DASH_ARRAY=DEFAULT_DASH_ARRAY;
  private float DASH_OFFSET=DEFAULT_DASH_OFFSET;
  private int LINE_CAP=DEFAULT_LINE_CAP; // BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND or BasicStroke.CAP_SQUARE
  private int LINE_JOIN=DEFAULT_LINE_JOIN; // BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL, or BasicStroke.JOIN_MITER
  private float MITER_LIMIT=DEFAULT_MITER_LIMIT;
  private float OPACITY=DEFAULT_OPACITY;
  private float WIDTH=DEFAULT_WIDTH;
  
  public boolean getEnabled() { return(ENABLED); }
  public Color getColor() { return(COLOR); }
  public float[] getDashArray() {return(DASH_ARRAY); }
  public float getDashOffset() { return(DASH_OFFSET); }
  public int getLineCap() { return(LINE_CAP); }
  public int getLineJoin() { return(LINE_JOIN); }
  public float getMiterLimit() { return(MITER_LIMIT); }
  public float getOpacity() { return(OPACITY); }
  public float getStrokeWidth() { return(WIDTH); }
  
  public void setEnabled(boolean newEnabled)
  { if(ENABLED!=newEnabled) { ENABLED=newEnabled; FIRE_CHANGE_EVENT("ENABLED"); } }
  public void setColor(Color newColor)
  { boolean changed=false;
    if(COLOR==null) changed=(newColor!=null);
    else changed=(!COLOR.equals(newColor));
	if(changed) { COLOR=newColor; FIRE_CHANGE_EVENT("COLOR"); }
  }
  public void setDashArray(float[] newDashArray) { if(DASH_ARRAY!=newDashArray) { DASH_ARRAY=newDashArray; FIRE_CHANGE_EVENT("DASH_ARRAY"); } }
  public void setDashOffset(float newDashOffset) { if(DASH_OFFSET!=newDashOffset) { DASH_OFFSET=newDashOffset; FIRE_CHANGE_EVENT("DASH_OFFSET"); } }
  public void setLineCap(int newLineCap) { if(LINE_CAP!=newLineCap) { LINE_CAP=newLineCap; FIRE_CHANGE_EVENT("LINE_CAP"); } }
  public void setLineJoin(int newLineJoin) { if(LINE_JOIN!=newLineJoin) { LINE_JOIN=newLineJoin; FIRE_CHANGE_EVENT("LINE_JOIN"); } }
  public void setMiterLimit(float newMiterLimit) { if(MITER_LIMIT!=newMiterLimit) { MITER_LIMIT=newMiterLimit; FIRE_CHANGE_EVENT("MITER_LIMIT"); } }
  public void setOpacity(float newOpacity) { if(OPACITY!=newOpacity) { OPACITY=newOpacity; FIRE_CHANGE_EVENT("OPACITY"); } }
  public void setStrokeWidth(float newWidth) { if(WIDTH!=newWidth) { WIDTH=newWidth; FIRE_CHANGE_EVENT("WIDTH"); } }
  
  // *************************************************************************
  // *** CONSTRUCTOR: SvgStroke
  // *************************************************************************
  public SvgStroke()
  { EditorProperties.loadStroke(this); }
  
  public SvgStroke(SVGElement svgElement)
  { String atr=svgElement.getAttribute("stroke"); if(atr.length()>0) setColor(Util.decodeRGB(atr));
    atr=svgElement.getAttribute("stroke-opacity"); if(atr.length()>0) setOpacity(Float.parseFloat(atr));
    atr=svgElement.getAttribute("stroke-width"); if(atr.length()>0) setStrokeWidth(Float.parseFloat(atr));
    atr=svgElement.getAttribute("stroke-linecap"); if(atr.length()>0) setLineCap(Util.lineCap(atr));
    atr=svgElement.getAttribute("stroke-linejoin"); if(atr.length()>0) setLineJoin(Util.lineJoin(atr));
    atr=svgElement.getAttribute("stroke-miterlimit"); if(atr.length()>0) setMiterLimit(Float.parseFloat(atr));
    atr=svgElement.getAttribute("stroke-dasharray"); if(atr.length()>0) setDashArray(Util.decodeFloats(atr,"none"));
    atr=svgElement.getAttribute("stroke-dashoffset"); if(atr.length()>0) setDashOffset(Float.parseFloat(atr));
    setEnabled((getColor()!=null));
  }

  public void appendSVGAttributes(Element svgElement)
  {	if(!getEnabled())
    { svgElement.setAttributeNS(null,"stroke","none");
	  return;
    }
	svgElement.setAttributeNS(null,"stroke",Util.encodeRGB(getColor()));
	if(getStrokeWidth()!=DEFAULT_WIDTH) svgElement.setAttributeNS(null,"stroke-width",""+getStrokeWidth());
	if(getOpacity()!=DEFAULT_OPACITY) svgElement.setAttributeNS(null,"stroke-opacity",""+getOpacity());
	if(getLineCap()!=DEFAULT_LINE_CAP) svgElement.setAttributeNS(null,"stroke-linecap",Util.lineCap(getLineCap()));
	if(getLineJoin()!=DEFAULT_LINE_JOIN) svgElement.setAttributeNS(null,"stroke-linejoin",Util.lineJoin(getLineJoin()));
	if(getMiterLimit()!=DEFAULT_MITER_LIMIT) svgElement.setAttributeNS(null,"stroke-miterlimit",""+getMiterLimit());
	if(getDashArray()!=DEFAULT_DASH_ARRAY) svgElement.setAttributeNS(null,"stroke-dasharray",Util.encodeFloats(getDashArray(),"none"));
	if(getDashOffset()!=DEFAULT_DASH_OFFSET) svgElement.setAttributeNS(null,"stroke-dashoffset",""+getDashOffset());
  }
  
  public Color getRGBAColor()
  { return(Util.getRGBAColor(getColor(),getOpacity())); }

  public BasicStroke getBasicStroke()
  { return(new BasicStroke(getStrokeWidth(),getLineCap(),getLineJoin(),getMiterLimit(),getDashArray(),getDashOffset())); }

  

  public SvgStroke copy()
  { SvgStroke stroke=new SvgStroke();
    copy(this,stroke);
	return(stroke);  
  }

  public static void copy(SvgStroke from,SvgStroke to)
  { to.setColor(from.getColor());
    to.setDashArray(from.getDashArray());
    to.setDashOffset(from.getDashOffset());
    to.setLineCap(from.getLineCap());
    to.setLineJoin(from.getLineJoin());
    to.setMiterLimit(from.getMiterLimit());
    to.setOpacity(from.getOpacity());
    to.setStrokeWidth(from.getStrokeWidth());
  }
  
  
  //********************************************************************
  //*** INNER CLASS: StrokeDialog
  //********************************************************************
  public class StrokeDialog extends EditValueTypeDialog
  { static final long serialVersionUID=0; // AD'HOC
    private StrokeAttributesPanel strokeAttributesPanel;
    private StrokePreViewPanel strokePreViewPanel;
    private JButton okBtn;
    private JButton cancelBtn;
    // Initial Values:
    private SvgStroke initialStroke;
    protected void saveInitialValues()
    { initialStroke=SvgStroke.this.copy(); }
    protected void restoreInitialValues()
    { copy(initialStroke,SvgStroke.this);    	  
    }

    // ******************************************************************
    // *** CONSTRUCTOR: StrokeDialog
    // ******************************************************************
    public StrokeDialog(final boolean saveToProperties)
    { super(SvgStroke.this,"Edit Stroke Color and Attributes");
      setBounds(300,200,445,750); // Popup to: x,y,width,height
      
      final JColorChooser colorChooser=new JColorChooser(getColor());
	  final ChangeListener colorChangeListener=new ChangeListener()
	  { public void stateChanged(ChangeEvent e)
	    { setColor(colorChooser.getColor()); }
	  };
      colorChooser.getSelectionModel().addChangeListener(colorChangeListener);
      strokePreViewPanel=new StrokePreViewPanel();
	  colorChooser.setPreviewPanel(strokePreViewPanel);

      JPanel buttons=new JPanel();
      buttons.add(okBtn=new JButton("Ok"));
      okBtn.addActionListener(new ActionListener()
      { public void actionPerformed(ActionEvent e)
        { if(saveToProperties) EditorProperties.storeStroke(SvgStroke.this);
          close();
        }
      });
      buttons.add(cancelBtn=new JButton("Cancel"));
      cancelBtn.addActionListener(new ActionListener()
      { public void actionPerformed(ActionEvent e) { cancel(); } });
      setLayout(new BorderLayout());
      strokeAttributesPanel=new StrokeAttributesPanel("Stroke");
      add(strokeAttributesPanel,BorderLayout.CENTER);//.NORTH);
      add(colorChooser, BorderLayout.NORTH);//.CENTER);
      add(buttons, BorderLayout.SOUTH);
      setAlwaysOnTop(true);
      setVisible(true);
    }
  }

  // ****************************************************************
  // *** INNER CLASS : StrokeAttributesPanel
  // ****************************************************************
  private class StrokeAttributesPanel extends JPanel
  { static final long serialVersionUID=0; // AD'HOC

    public StrokeAttributesPanel(String title)
    { setBorder(Util.getTitleBorder(title));
      setPreferredSize(new Dimension(350,170+10));
      setLayout(new GridLayout(7,2));//(1,2));
      
      add(new JLabel("Stroke Width:"));
      add(new ValueSpinner(getStrokeWidth(),1.0,10.0,0.1)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value)
        { setStrokeWidth((float)value); }	
      });
        
      add(new JLabel("Stroke Opacity:"));
      add(new ValueSpinner(getOpacity(),0.0,1.0,0.1)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value)
        { setOpacity((float)value); }	
      });
      
      add(new JLabel("Line Cap:"));
      { String[] vals={"butt","round","square"};
        final JComboBox comboBox=new JComboBox(vals);
        switch(getLineCap())
        { case BasicStroke.CAP_BUTT: comboBox.setSelectedIndex(0); break;
          case BasicStroke.CAP_ROUND: comboBox.setSelectedIndex(1); break;
          case BasicStroke.CAP_SQUARE: comboBox.setSelectedIndex(2); break;
        }
        comboBox.addActionListener(new ActionListener()
        { public void actionPerformed(ActionEvent e)
          { String selected=(String)comboBox.getSelectedItem();
            if(selected.equalsIgnoreCase("butt")) setLineCap(BasicStroke.CAP_BUTT);
            if(selected.equalsIgnoreCase("round")) setLineCap(BasicStroke.CAP_ROUND);
            if(selected.equalsIgnoreCase("square")) setLineCap(BasicStroke.CAP_SQUARE);
          }
        });
        add(comboBox);
      }
      
      add(new JLabel("Line Join:"));
      { String[] vals={"miter","round","bevel"};
        final JComboBox comboBox=new JComboBox(vals);
        switch(getLineJoin())
        { case BasicStroke.JOIN_MITER: comboBox.setSelectedIndex(0); break;
          case BasicStroke.JOIN_ROUND: comboBox.setSelectedIndex(1); break;
          case BasicStroke.JOIN_BEVEL: comboBox.setSelectedIndex(2); break;
        }
        comboBox.addActionListener(new ActionListener()
        { public void actionPerformed(ActionEvent e)
          { String selected=(String)comboBox.getSelectedItem();
            if(selected.equalsIgnoreCase("miter")) setLineJoin(BasicStroke.JOIN_MITER);
            if(selected.equalsIgnoreCase("round")) setLineJoin(BasicStroke.JOIN_ROUND);
            if(selected.equalsIgnoreCase("bevel")) setLineJoin(BasicStroke.JOIN_BEVEL);
          }
        });
        add(comboBox);
      } 
      
      add(new JLabel("Stroke Miter Limit:"));
      add(new ValueSpinner(getMiterLimit(),1.0,10.0,0.1)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value)
        { setMiterLimit((float)value); }	
      });
  
      add(new JLabel("Dash Array:"));
      { String[] vals={"none","dash dash","dash dot"};
        final float[] dashDash={20,10};
        final float[] dashDot={10,5,2,5};
        int dashIndex=0;
        String ds=Util.encodeFloats(getDashArray(),"none");
//      Debug.TRACE("*** DASH - ds="+ds);
//      Debug.TRACE("*** DASH - dashDash="+Util.encodeFloats(dashDash,"none"));
//      Debug.TRACE("*** DASH - dashDot="+Util.encodeFloats(dashDot,"none"));
        if(ds.equals(Util.encodeFloats(dashDash,"none"))) dashIndex=1;
        if(ds.equals(Util.encodeFloats(dashDot,"none"))) dashIndex=2;
        final JComboBox comboBox=new JComboBox(vals);
        comboBox.setSelectedIndex(dashIndex);
        comboBox.addActionListener(new ActionListener()
        { public void actionPerformed(ActionEvent e)
          { String selected=(String)comboBox.getSelectedItem();
            if(selected.equalsIgnoreCase("none")) setDashArray(null);
            if(selected.equalsIgnoreCase("dash dash")) setDashArray(dashDash);
            if(selected.equalsIgnoreCase("dash dot")) setDashArray(dashDot);
          }
        });
        add(comboBox);
      }
      
      add(new JLabel("Dash Offset:"));
      add(new ValueSpinner(getDashOffset(),0.0,10.0,0.1)
      { static final long serialVersionUID=0; // AD'HOC
        public void update(double value)
        { setDashOffset((float)value); }	
      });
    }
  }

  // ****************************************************************
  // *** INNER CLASS: StrokePreViewPanel
  // ****************************************************************
  public class StrokePreViewPanel extends JPanel
  { static final long serialVersionUID=0; // AD'HOC
    // **************************************************************
    // *** CONSTRUCTOR: StrokePreViewPanel
    // **************************************************************
    public StrokePreViewPanel()
    { setBorder(new EmptyBorder(10,5,5,5));
      setPreferredSize(new Dimension(350,200));
    }
    
    public void paint(Graphics g)
    { super.paint(g);
      int w=getWidth();
      int h=getHeight();
      g.setColor(Color.white);
      int cx=w/2;
      int cy=h/2;
      int dx=w/40;
      int dy=h/40;
      g.fillRect(10,20,w-20,h-30);
      GeneralPath path=new GeneralPath();
      path.moveTo(cx-10*dx,cy-10*dy);
      path.lineTo(cx+10*dx,cy-10*dy);
      path.moveTo(cx-10*dx,cy);
      path.lineTo(cx+5*dx,cy);
      path.lineTo(cx+5*dx,cy+10*dy);
      path.closePath();
      g.setColor(getRGBAColor());
      ((Graphics2D)g).setStroke(getBasicStroke());
	  ((Graphics2D)g).draw(path);    	  
    }
  }

}
